<script setup lang="tsx">
import { computed, reactive, ref, watch } from 'vue';
import type { UploadFileInfo } from 'naive-ui';
import { useFormRules, useNaiveForm } from '@/hooks/common/form';
import { $t } from '@/locales';
import {
  type OAuth2Client,
  type OAuth2ClientEditAndCreateParams,
  createOAuth2Client,
  updateOAuth2Client
} from '@/service/api';
import { DICT_TYPE, getIntDictOptions, getStrDictOptions } from '@/utils/dict';
import { useUpload } from '@/components/custom/upload-file/src/useUpload';

defineOptions({
  name: 'OAuth2ClientOperateModal'
});

export type OperateType = NaiveUI.TableOperateType;

interface Props {
  /** the type of operation */
  operateType: OperateType;
  /** the edit menu data or the parent menu data when adding a child menu */
  rowData?: OAuth2Client | null;
}

const props = defineProps<Props>();

interface Emits {
  (e: 'submitted'): void;
}

const emit = defineEmits<Emits>();

const visible = defineModel<boolean>('visible', {
  default: false
});

const { formRef, validate, restoreValidation } = useNaiveForm();
const { defaultRequiredRule } = useFormRules();

const submitLoading = ref(false);

const title = computed(() => {
  const titles: Record<OperateType, string> = {
    add: $t('page.manage.oauth2.client.addClient'),
    edit: $t('page.manage.oauth2.client.editClient')
  };
  return titles[props.operateType];
});

type Model = Exclude<OAuth2ClientEditAndCreateParams, 'clientIdIssuedAt'>;

const model: Model = reactive(createDefaultModel());
const fileListRef = ref<UploadFileInfo[]>([]);

function createDefaultModel(): Model {
  return {
    clientId: '',
    clientSecret: '',
    clientName: '',
    clientSecretExpiresAt: null,
    clientAuthenticationMethods: '',
    authorizationGrantTypes: [],
    redirectUris: '',
    postLogoutRedirectUris: '',
    scopes: '',
    clientLogo: '',
    clientDescription: '',
    accessTokenTimeToLive: 3600,
    refreshTokenTimeToLive: 3600,
    status: 1
  };
}

type RuleKey = Extract<
  keyof Model,
  | 'clientId'
  | 'clientSecret'
  | 'clientName'
  | 'clientSecretExpiresAt'
  | 'clientAuthenticationMethods'
  | 'authorizationGrantTypes'
  | 'accessTokenTimeToLive'
  | 'refreshTokenTimeToLive'
  | 'status'
>;

const rules: Record<RuleKey, App.Global.FormRule> = {
  clientId: defaultRequiredRule,
  clientSecret: defaultRequiredRule,
  clientName: defaultRequiredRule,
  clientSecretExpiresAt: defaultRequiredRule,
  clientAuthenticationMethods: defaultRequiredRule,
  authorizationGrantTypes: defaultRequiredRule,
  accessTokenTimeToLive: defaultRequiredRule,
  refreshTokenTimeToLive: defaultRequiredRule,
  status: defaultRequiredRule
};

function closeDrawer() {
  submitLoading.value = false;
  visible.value = false;
}

async function handleSubmit() {
  await validate();

  submitLoading.value = true;

  if (props.operateType === 'edit') {
    // request
    const { error } = await updateOAuth2Client(model.id as number, model);

    if (!error) {
      window.$message?.success($t('common.updateSuccess'));
      closeDrawer();
      emit('submitted');
    }
  } else {
    const { error } = await createOAuth2Client(model);
    if (!error) {
      window.$message?.success($t('common.addSuccess'));
      closeDrawer();
      emit('submitted');
    }
  }
}

async function handleInitModel() {
  fileListRef.value = [];
  Object.assign(model, createDefaultModel());

  if (!props.rowData) return;

  if (props.operateType === 'edit' && props.rowData) {
    Object.assign(model, props.rowData);

    fileListRef.value.push({
      id: (model.id ?? '').toString(),
      name: (model.clientName ?? '').toString(),
      status: 'finished',
      url: (model.clientLogo ?? '').toString()
    });
  }
}

const { uploadUrl, httpRequest } = useUpload();
/** 文件上传成功处理 */
const handleSuccess = ({ file }: { file: UploadFileInfo; event?: ProgressEvent }) => {
  // 提示成功，并刷新
  if (fileListRef.value.length > 1) {
    window.$message?.success($t('common.uploadFileSuccess') + $t('common.latterOverrideFormer'));
  } else {
    window.$message?.success($t('common.uploadFileSuccess'));
  }

  model.clientLogo = file.url;

  fileListRef.value = [
    {
      id: (model.id ?? '').toString(),
      name: (model.clientName ?? '').toString(),
      status: 'finished',
      url: (model.clientLogo ?? '').toString()
    }
  ];
};

/** 上传错误提示 */
const handleError = (): void => {
  window.$message?.error($t('common.uploadFileFailure'));
};

watch(visible, () => {
  if (visible.value) {
    handleInitModel();
    restoreValidation();
  }
});
</script>

<template>
  <NModal v-model:show="visible" :title="title" preset="card" :mask-closable="false" class="w-250">
    <NScrollbar class="max-h-200 pr-20px">
      <NForm ref="formRef" :model="model" :rules="rules" label-placement="left" label-width="auto">
        <NFormItem :label="$t('page.manage.oauth2.client.clientId')" path="clientId">
          <NInput v-model:value="model.clientId" :placeholder="$t('page.manage.oauth2.client.form.clientId')" />
        </NFormItem>
        <NFormItem :label="$t('page.manage.oauth2.client.clientSecret')" path="clientSecret">
          <NInput v-model:value="model.clientSecret" :placeholder="$t('page.manage.oauth2.client.form.clientSecret')" />
        </NFormItem>
        <NFormItem :label="$t('page.manage.oauth2.client.clientName')" path="clientName">
          <NInput v-model:value="model.clientName" :placeholder="$t('page.manage.oauth2.client.form.clientName')" />
        </NFormItem>
        <NFormItem :label="$t('page.manage.oauth2.client.clientLogo')" path="clientLogo">
          <NUpload
            v-model:file-list="fileListRef"
            accept="image/apng,image/bmp,image/gif,image/jpeg,image/pjpeg,image/png,image/svg+xml,image/tiff,image/webp,image/x-icon"
            :action="uploadUrl"
            :custom-request="httpRequest"
            list-type="image-card"
            :placeholder="$t('page.manage.oauth2.client.form.clientLogo')"
            @finish="handleSuccess"
            @error="handleError"
          />
        </NFormItem>
        <NFormItem :label="$t('page.manage.oauth2.client.clientDescription')" path="clientDescription">
          <NInput
            v-model:value="model.clientDescription"
            :placeholder="$t('page.manage.oauth2.client.form.clientDescription')"
          />
        </NFormItem>
        <NFormItem :label="$t('page.manage.dict.type.status')" path="status">
          <NRadioGroup v-model:value="model.status">
            <NRadio
              v-for="item in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
              :key="item.value"
              :value="item.value"
              :label="item.label"
            />
          </NRadioGroup>
        </NFormItem>
        <NFormItem :label="$t('page.manage.oauth2.client.accessTokenTimeToLive')" path="accessTokenTimeToLive">
          <NInputNumber
            v-model:value="model.accessTokenTimeToLive"
            :placeholder="$t('page.manage.oauth2.client.form.accessTokenTimeToLive')"
          />
        </NFormItem>
        <NFormItem :label="$t('page.manage.oauth2.client.refreshTokenTimeToLive')" path="refreshTokenTimeToLive">
          <NInputNumber
            v-model:value="model.refreshTokenTimeToLive"
            :placeholder="$t('page.manage.oauth2.client.form.refreshTokenTimeToLive')"
          />
        </NFormItem>
        <NFormItem :label="$t('page.manage.oauth2.client.authorizationGrantTypes')" path="authorizationGrantTypes">
          <NSelect
            v-model:value="model.authorizationGrantTypes"
            :options="getStrDictOptions(DICT_TYPE.SYSTEM_OAUTH2_GRANT_TYPE)"
            clearable
            :placeholder="$t('page.manage.oauth2.client.form.authorizationGrantTypes')"
          />
        </NFormItem>
        <NFormItem :label="$t('page.manage.oauth2.client.clientScope')" path="scopes">
          <NInput v-model:value="model.scopes" :placeholder="$t('page.manage.oauth2.client.form.clientScope')" />
        </NFormItem>
        <NFormItem :label="$t('page.manage.oauth2.client.redirectUris')" path="redirectUris">
          <NInput v-model:value="model.redirectUris" :placeholder="$t('page.manage.oauth2.client.form.redirectUris')" />
        </NFormItem>
      </NForm>
    </NScrollbar>
    <template #footer>
      <NSpace justify="end" :size="16">
        <NButton @click="closeDrawer">{{ $t('common.cancel') }}</NButton>
        <NButton type="primary" :loading="submitLoading" @click="handleSubmit">{{ $t('common.confirm') }}</NButton>
      </NSpace>
    </template>
  </NModal>
</template>

<style scoped>
.flex-end {
  display: flex;
  justify-content: flex-end;
}
</style>
